#include "stdafx.h"
#include "Shape.h"

void GetShapeFrameInfo( const SHAPEENTRY* shapeEntry, long shapeLength, int frameIndex,
	U7VECTOR2& hotspot, U7VECTOR2& dimension )
{
	if ( shapeLength != shapeEntry->size ) // tile
	{
		hotspot.x = PIXELS_PER_TILE_IN_WIDTH - 1;
		hotspot.y = PIXELS_PER_TILE_IN_HEIGHT - 1;;
		dimension.x = PIXELS_PER_TILE_IN_WIDTH;
		dimension.y = PIXELS_PER_TILE_IN_HEIGHT;
	}
	else if ( shapeLength == shapeEntry->size
		&& frameIndex < (int)(shapeEntry->offset[0]/sizeof(long)-1) ) // object
	{
		PFRAMEENTRY frameEntry = (PFRAMEENTRY)((char*)shapeEntry + shapeEntry->offset[frameIndex]);
		GetShapeFrameInfo( frameEntry, hotspot, dimension );
	}
	else
	{
		hotspot.x = 0;
		hotspot.y = 0;
		dimension.x = 0;
		dimension.y = 0;
	}
}

static
void DrawShapeTileNoClipped( PGRAPHIC pGraphic, const TILE tile, int x, int y )
{
	char* dst = (char*)pGraphic->pBits + y * pGraphic->pitch + x;
	for ( int h = 0; h < PIXELS_PER_TILE_IN_HEIGHT; h++ )
	{
		memcpy( dst, tile[h], PIXELS_PER_TILE_IN_WIDTH );
		dst += pGraphic->pitch;
	}
}

static
void DrawShapeTileClipped( PGRAPHIC pGraphic, const TILE tile, int x, int y )
{
	char* src = tile[0];
	char* dst = (char*)pGraphic->pBits + y * pGraphic->pitch + x;

	int xlen = PIXELS_PER_TILE_IN_WIDTH;
	int ylen = PIXELS_PER_TILE_IN_HEIGHT;

	if ( x < 0 )
	{
		xlen -= -x;
		src  += -x;
		dst  += -x;
		x = 0;
	}
	if ( x + xlen > pGraphic->width )
	{
		xlen = pGraphic->width - x;
	}
	if ( y < 0 )
	{
		ylen -= -y;
		src += (-y) * PIXELS_PER_TILE_IN_WIDTH;
		dst += (-y) * pGraphic->pitch;
		y = 0;
	}
	if ( y + ylen > pGraphic->height )
	{
		ylen = pGraphic->height - y;
	}
	while( ylen-- > 0 )
	{
		memcpy( dst, src, xlen );
		src += PIXELS_PER_TILE_IN_WIDTH;
		dst += pGraphic->pitch;
	}
}

void DrawShapeTile( PGRAPHIC pGraphic, const TILE tile, int x, int y )
{
	LONG lWidth = pGraphic->width;
	LONG lHeight = pGraphic->height;

	long lb = x - PIXELS_PER_TILE_IN_WIDTH + 1;  // left boundary
	long rb = x;                   // right boundary
	long tb = y - PIXELS_PER_TILE_IN_HEIGHT + 1; // top boundary
	long bb = y;                   // bottom boundary

	if ( lb >= lWidth || rb < 0 || tb >= lHeight || bb < 0 )
	{
		// out of bound entirely, do nothing
	}
	else if ( lb < 0 || rb >= lWidth || tb < 0 || bb >= lHeight )
	{
		// out of bound partially
		DrawShapeTileClipped( pGraphic, tile, lb, tb );
	}
	else
	{
		// inside the boundary
		DrawShapeTileNoClipped( pGraphic, tile, lb, tb );
	}
}

static
void DrawShapeFrameNoClipped( PGRAPHIC pGraphic, const FRAMEENTRY* pFrameEntry, int x, int y )
{
	// pGraphic: represent the graphic object for drawing
	// pFrameEntry: represent the frame data
	// x, y: the position to put the frame which anchored the hotspot of the frame

	// get the first slice from FRAMEENTRY::slice
	PSLICEENTRY slice = (PSLICEENTRY)pFrameEntry->slice;
	// loop through all slices until the length of the slice is 0
	while ( slice->length != 0 )
	{
		int xoff = x + slice->offsetX; // the start position of the graphic in x axis
		int yoff = y + slice->offsetY; // the start position of the graphic in y axis

		// pointer to the start address of the graphic memory
		PBYTE pBits = (PBYTE)pGraphic->pBits + pGraphic->pitch * yoff + xoff;

		if ( slice->type == 0 )
		{
			// this is the standard slice, no compressed.
			// copy the pixels to the graphic memory directly.
			memcpy( pBits, slice->content, slice->length );
			// move to next slice
			slice = (PSLICEENTRY)((char*)slice + sizeof(SLICEENTRY) - 1 + slice->length);
		}
		else
		{
			// this is the compressed slice.
			int slength = slice->length;
			PSLICECONTENT scontent = (PSLICECONTENT)slice->content;
			// loop though the sub blocks(SLICECONTENT) to decode pixels.
			while ( slength > 0 )
			{
				int len = scontent->length; // save to a temporary variable

				if ( scontent->type == 0 ) {
					// this is the standard block, no compressed.
					// copy the pixels to the graphic memory directly.
					memcpy( pBits, scontent->pixel, len );
					// move to next block
					scontent = (PSLICECONTENT)((char*)scontent + sizeof(SLICECONTENT) - 1 + len);
				}
				else {
					// this is the repeated pixel blcok
					// set the pixels in the length as the specific color
					memset( pBits, scontent->pixel[0], len );
					// move to next block
					scontent++;
				}
					
				slength -= len;
				pBits += len;
			}
			ASSERT(slength==0); // make sure no surprised to decode data
			slice = (PSLICEENTRY)scontent; // assign the next slice.
		}
	}
}

static
void DrawShapeFrameClipped( PGRAPHIC pGraphic, const FRAMEENTRY* pFrameEntry, int x, int y )
{
	LONG lWidth = pGraphic->width;
	LONG lHeight = pGraphic->height;
	LONG lPitch = pGraphic->pitch;

	PSLICEENTRY slice = (PSLICEENTRY)pFrameEntry->slice;
	while ( slice->length != 0 )
	{
		int xoff = x + slice->offsetX;
		int yoff = y + slice->offsetY;

		if ( yoff >= 0 && yoff < lHeight )
		{
			PBYTE pBits = (PBYTE)pGraphic->pBits + pGraphic->pitch * yoff + xoff;

			if ( slice->type == 0 )
			{
				PBYTE pSrc = (PBYTE)slice->content;
				PBYTE pDst = pBits;
				int slen = slice->length;
				if ( xoff < 0 )
				{
					pDst -= xoff;
					pSrc -= xoff;
					slen += xoff;
					xoff = 0;
				}
				if ( xoff + slen > lWidth )
				{
					slen = lWidth - xoff;
				}
				if ( slen > 0 )
				{
					memcpy( pDst, pSrc, slen );
				}
				slice = (PSLICEENTRY)((char*)slice + sizeof(SLICEENTRY) - 1 + slice->length);
			}
			else
			{
				int slength = slice->length;
				PSLICECONTENT scontent = (PSLICECONTENT)slice->content;
				while ( slength > 0 )
				{
					int sclen = scontent->length;

					if ( scontent->type == 0 ) {
						PBYTE pSrc = (PBYTE)scontent->pixel;
						PBYTE pDst = pBits;
						int curLen = sclen;
						int xtmp = xoff;
						if ( xtmp < 0 )
						{
							pDst -= xtmp;
							pSrc -= xtmp;
							curLen += xtmp;
							xtmp = 0;
						}
						if ( xtmp + curLen > lWidth )
						{
							curLen = lWidth - xtmp;
						}
						if ( curLen > 0 )
						{
							memcpy( pDst, pSrc, curLen );
						}
						scontent = (PSLICECONTENT)((char*)scontent + sizeof(SLICECONTENT) - 1 + sclen);
					}
					else {
						PBYTE pDst = pBits;
						int curLen = sclen;
						int xtmp = xoff;
						if ( xtmp < 0 )
						{
							pDst -= xoff;
							curLen += xoff;
							xtmp = 0;
						}
						if ( xtmp + curLen > lWidth )
						{
							curLen = lWidth - xtmp;
						}
						if ( curLen > 0 )
						{
							memset( pDst, scontent->pixel[0], curLen );
						}
						scontent++;
					}
					
					slength -= sclen;
					pBits += sclen;
					xoff += sclen;
				}
				ASSERT(slength==0);
				slice = (PSLICEENTRY)scontent;
			}
		}
		else
		{
			// skip entire slice
			if ( slice->type == 0 ) 	{
				slice = (PSLICEENTRY)((char*)slice + sizeof(SLICEENTRY) - 1 + slice->length);
			}
			else {
				int slength = slice->length;
				PSLICECONTENT scontent = (PSLICECONTENT)slice->content;
				while ( slength > 0 ) {
					int len = scontent->length;
					if ( scontent->type == 0 ) {
						scontent = (PSLICECONTENT)((char*)scontent + sizeof(SLICECONTENT) - 1 + len);
					}
					else {
						scontent++;
					}					
					slength -= len;
				}
				ASSERT(slength==0);
				slice = (PSLICEENTRY)scontent;
			}
		}
	}
}

void DrawShapeFrame( PGRAPHIC pGraphic, const FRAMEENTRY* frameEntry, int x, int y )
{
	long lWidth = pGraphic->width;
	long lHeight = pGraphic->height;
	long lPitch = pGraphic->pitch;

	long lb = x - frameEntry->leftX;	// left boundary
	long rb = x + frameEntry->rightX;	// right boundary
	long tb = y - frameEntry->topY;		// top boundary
	long bb = y + frameEntry->bottomY;	// bottom boundary

	if ( lb >= lWidth || rb < 0 || tb >= lHeight || bb < 0 )
	{
		// out of bound entirely, do nothing
	}
	else if ( lb < 0 || rb >= lWidth || tb < 0 || bb >= lHeight )
	{
		// out of bound partially
		DrawShapeFrameClipped( pGraphic, frameEntry, x, y );
	}
	else
	{
		// inside the boundary
		DrawShapeFrameNoClipped( pGraphic, frameEntry, x, y );
	}
}

static
void DrawShapeFrameNoClipped(
	PGRAPHIC pGraphic,
	const FRAMEENTRY* pFrameEntry,
	int x, int y,
	XFORMTBL xform, int tbls )
{
	// pGraphic: represent the graphic object for drawing
	// pFrameEntry: represent the frame data
	// x, y: the position to put the frame which anchored the hotspot of the frame

	// get the first slice from FRAMEENTRY::slice
	PSLICEENTRY slice = (PSLICEENTRY)pFrameEntry->slice;
	// loop through all slices until the length of the slice is 0
	while ( slice->length != 0 )
	{
		int xoff = x + slice->offsetX; // the start position of the graphic in x axis
		int yoff = y + slice->offsetY; // the start position of the graphic in y axis

		// pointer to the start address of the graphic memory
		PBYTE pBits = (PBYTE)pGraphic->pBits + pGraphic->pitch * yoff + xoff;

		if ( slice->type == 0 )
		{
			// this is the standard slice, no compressed.
			// copy the pixels to the graphic memory directly.
			CopyTranslucentLine( pBits, slice->content, slice->length, xform, tbls );
			// move to next slice
			slice = (PSLICEENTRY)((char*)slice + sizeof(SLICEENTRY) - 1 + slice->length);
		}
		else
		{
			// this is the compressed slice.
			int slength = slice->length;
			PSLICECONTENT scontent = (PSLICECONTENT)slice->content;
			// loop though the sub blocks(SLICECONTENT) to decode pixels.
			while ( slength > 0 )
			{
				int len = scontent->length; // save to a temporary variable

				if ( scontent->type == 0 ) {
					// this is the standard block, no compressed.
					// copy the pixels to the graphic memory directly.
					CopyTranslucentLine( pBits, scontent->pixel, len, xform, tbls );
					// move to next block
					scontent = (PSLICECONTENT)((char*)scontent + sizeof(SLICECONTENT) - 1 + len);
				}
				else {
					// this is the repeated pixel blcok
					// set the pixels in the length as the specific color
					SetTranslucentLine( pBits, scontent->pixel[0], len, xform, tbls );
					// move to next block
					scontent++;
				}
					
				slength -= len;
				pBits += len;
			}
			ASSERT(slength==0); // make sure no surprised to decode data
			slice = (PSLICEENTRY)scontent; // assign the next slice.
		}
	}
}

static
void DrawShapeFrameClipped(
	PGRAPHIC pGraphic,
	const FRAMEENTRY* pFrameEntry,
	int x, int y,
	XFORMTBL xform, int tbls )
{
	LONG lWidth = pGraphic->width;
	LONG lHeight = pGraphic->height;
	LONG lPitch = pGraphic->pitch;

	PSLICEENTRY slice = (PSLICEENTRY)pFrameEntry->slice;
	while ( slice->length != 0 )
	{
		int xoff = x + slice->offsetX;
		int yoff = y + slice->offsetY;

		if ( yoff >= 0 && yoff < lHeight )
		{
			PBYTE pBits = (PBYTE)pGraphic->pBits + pGraphic->pitch * yoff + xoff;

			if ( slice->type == 0 )
			{
				PBYTE pSrc = (PBYTE)slice->content;
				PBYTE pDst = pBits;
				int slen = slice->length;
				if ( xoff < 0 )
				{
					pDst -= xoff;
					pSrc -= xoff;
					slen += xoff;
					xoff = 0;
				}
				if ( xoff + slen > lWidth )
				{
					slen = lWidth - xoff;
				}
				if ( slen > 0 )
				{
					CopyTranslucentLine( pDst, pSrc, slen, xform, tbls );
				}
				slice = (PSLICEENTRY)((char*)slice + sizeof(SLICEENTRY) - 1 + slice->length);
			}
			else
			{
				int slength = slice->length;
				PSLICECONTENT scontent = (PSLICECONTENT)slice->content;
				while ( slength > 0 )
				{
					int sclen = scontent->length;

					if ( scontent->type == 0 ) {
						PBYTE pSrc = (PBYTE)scontent->pixel;
						PBYTE pDst = pBits;
						int curLen = sclen;
						int xtmp = xoff;
						if ( xtmp < 0 )
						{
							pDst -= xtmp;
							pSrc -= xtmp;
							curLen += xtmp;
							xtmp = 0;
						}
						if ( xtmp + curLen > lWidth )
						{
							curLen = lWidth - xtmp;
						}
						if ( curLen > 0 )
						{
							CopyTranslucentLine( pDst, pSrc, curLen, xform, tbls );
						}
						scontent = (PSLICECONTENT)((char*)scontent + sizeof(SLICECONTENT) - 1 + sclen);
					}
					else {
						PBYTE pDst = pBits;
						int curLen = sclen;
						int xtmp = xoff;
						if ( xtmp < 0 )
						{
							pDst -= xoff;
							curLen += xoff;
							xtmp = 0;
						}
						if ( xtmp + curLen > lWidth )
						{
							curLen = lWidth - xtmp;
						}
						if ( curLen > 0 )
						{
							SetTranslucentLine( pDst, scontent->pixel[0], curLen, xform, tbls );
						}
						scontent++;
					}
					
					slength -= sclen;
					pBits += sclen;
					xoff += sclen;
				}
				ASSERT(slength==0);
				slice = (PSLICEENTRY)scontent;
			}
		}
		else
		{
			// skip entire slice
			if ( slice->type == 0 ) 	{
				slice = (PSLICEENTRY)((char*)slice + sizeof(SLICEENTRY) - 1 + slice->length);
			}
			else {
				int slength = slice->length;
				PSLICECONTENT scontent = (PSLICECONTENT)slice->content;
				while ( slength > 0 ) {
					int len = scontent->length;
					if ( scontent->type == 0 ) {
						scontent = (PSLICECONTENT)((char*)scontent + sizeof(SLICECONTENT) - 1 + len);
					}
					else {
						scontent++;
					}					
					slength -= len;
				}
				ASSERT(slength==0);
				slice = (PSLICEENTRY)scontent;
			}
		}
	}
}

void DrawShapeFrame(
	PGRAPHIC pGraphic,
	const FRAMEENTRY* frameEntry,
	int x, int y,
	XFORMTBL xform, int tbls )
{
	long lWidth = pGraphic->width;
	long lHeight = pGraphic->height;
	long lPitch = pGraphic->pitch;

	long lb = x - frameEntry->leftX;	// left boundary
	long rb = x + frameEntry->rightX;	// right boundary
	long tb = y - frameEntry->topY;		// top boundary
	long bb = y + frameEntry->bottomY;	// bottom boundary

	if ( lb >= lWidth || rb < 0 || tb >= lHeight || bb < 0 )
	{
		// out of bound entirely, do nothing
	}
	else if ( lb < 0 || rb >= lWidth || tb < 0 || bb >= lHeight )
	{
		// out of bound partially
		DrawShapeFrameClipped( pGraphic, frameEntry, x, y, xform, tbls );
	}
	else
	{
		// inside the boundary
		DrawShapeFrameNoClipped( pGraphic, frameEntry, x, y, xform, tbls );
	}
}

void DrawShape( PGRAPHIC pGraphic, const SHAPEENTRY* shapeEntry, long shapeLength, long frameIndex, int nAnchorX, int nAnchorY )
{
	if ( IsShapeTile( shapeEntry, shapeLength ) ) // tile
	{
		TILE tile = GetShapeTile( shapeEntry, shapeLength, frameIndex );
		if (tile)
			DrawShapeTile( pGraphic, tile, nAnchorX, nAnchorY );
	}
	else // shape
	{
		PFRAMEENTRY frameEntry = GetShapeFrame( shapeEntry, shapeLength, frameIndex );
		if (frameEntry)
			DrawShapeFrame( pGraphic, frameEntry, nAnchorX, nAnchorY );
	}
}

bool HitTest( const FRAMEENTRY* frameEntry, int anchorX, int anchorY, int hitX, int hitY )
{
	if ( anchorX - frameEntry->leftX > hitX || anchorX + frameEntry->rightX < hitX )
		return false;

	if ( anchorY - frameEntry->topY > hitY || anchorY + frameEntry->bottomY < hitY )
		return false;

	PSLICEENTRY slice = (PSLICEENTRY)frameEntry->slice;
	while ( slice->length != 0 )
	{
		int xoff = anchorX + slice->offsetX;
		int yoff = anchorY + slice->offsetY;
		int slength = slice->length;
		PSLICECONTENT scontent = (PSLICECONTENT)slice->content;

		if ( yoff == hitY && xoff <= hitX && hitX < xoff + slength )
			return true;

		if ( slice->type == 0 )
		{			
			slice = (PSLICEENTRY)((char*)slice + sizeof(SLICEENTRY) - 1 + slength);
		}
		else
		{
			while ( slength > 0 )
			{
				int len = scontent->length;

				if ( scontent->type == 0 ) {
					scontent = (PSLICECONTENT)((char*)scontent + sizeof(SLICECONTENT) - 1 + len);
				}
				else {
					scontent++;
				}
				slength -= len;
			}
			slice = (PSLICEENTRY)scontent;
		}
	}

	return false;
}

char* CreateReflectShapeFrame( const FRAMEENTRY* frameEntry )
{
	U7VECTOR2 hotspot;
	U7VECTOR2 dimension;
	GetShapeFrameInfo( frameEntry, hotspot, dimension );

	int left = hotspot.x;
	int right = dimension.x - left - 1;
	int top = hotspot.y;
	int bottom = dimension.y - top - 1;

	// a fake graphic
	GRAPHIC g = {
		NULL,
		dimension.x, dimension.y, dimension.x,
		new char[dimension.x * dimension.y]
	};
	
	assert( g.pBits );

	memset( g.pBits, 0xFF, dimension.x * dimension.y );

	// draw the frame to the fake graphic
	DrawShapeFrame( &g, frameEntry, hotspot.x, hotspot.y );

	// allocate memory for the reflect shape frame
	PFRAMEENTRY newFrameEntry = (PFRAMEENTRY) new char[
		sizeof(FRAMEENTRY)
			+ sizeof(SLICEENTRY) * (dimension.x + 1)
			+ dimension.x * dimension.y
	];

	// set the hotspot and dimension
	newFrameEntry->leftX = top;
	newFrameEntry->rightX = bottom;
	newFrameEntry->topY = left;
	newFrameEntry->bottomY = right;

	// init as no slice.
	PSLICEENTRY slice =  (PSLICEENTRY)newFrameEntry->slice;
	slice->type = 0;
	slice->length = 0;

	int spotX = newFrameEntry->leftX;
	int spotY = newFrameEntry->topY;

	// convert from horizontal lines to vertical ones
	// that is, the first horizontal line is converted from the first vertiacal one.
	// line A'-C' is from line A-C
	//  A   B    
	//  +---+     A'     C'
	//  |   |     +------+
	//  |   |  => |      |
	//  |   |     +------+
	//  +---+     B'     D'
	//  C   D

	char* src = (char*)g.pBits;
	for ( int x = 0; x < dimension.x; x++ )
	{
		char* line = src;
		int y = 0;

		do
		{
			// skip transparent pixels
			while ( y < dimension.y )
			{
				if ( *line != -1 )
					break;
				line += dimension.x;
				y++;
			}

			if ( y == dimension.y )
				break; // skip the line

			// ecode slice
			slice->type = 0; // standard
			slice->length = 0;
			slice->offsetX = y - spotX;
			slice->offsetY = x - spotY;

			unsigned char* dst = slice->content;
			while ( y < dimension.y )
			{
				if ( *line == -1 ) // transparent pixel?
					break;
				*dst++ = *line;
				line += dimension.x;
				slice->length++;
				y++;
			}
			slice = (PSLICEENTRY)((char*)slice + sizeof(SLICEENTRY) - 1 + slice->length);
		}
		while( y < dimension.y );

		src++;
	}
	slice->type = 0;
	slice->length = 0;

	delete g.pBits;

	return (char*)newFrameEntry;
}




